/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!		
  \file			cpgdoc.h
  \date			Jan 2013
  \author		TNick

  \brief		Contains the definition for CpgDoc class


*//*


 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Please read COPYING and README files in root folder
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
#ifndef __CPGDOC_INC__
#define __CPGDOC_INC__
//
//
//
//
/*  INCLUDES    ------------------------------------------------------------ */

#include	<QObject>
#include	<QUndoStack>
#include	<QColor>

#include	<memtrack.h>
#include	<cpg/cpg_global.h>

#include	<cpg/logic/characteristics/pgcolor.h>
#include	<cpg/logic/characteristics/pglnpattern.h>
#include	<cpg/logic/characteristics/pglnpattscale.h>
#include	<cpg/logic/characteristics/pglnwidth.h>


/*  INCLUDES    ============================================================ */
//
//
//
//
/*  DEFINITIONS    --------------------------------------------------------- */

class QUrl;

namespace	cpg		{

class	PgItem;
class	PgItList;
class	PgLayer;
class	PgSel;
class	PgSpace;
class	LyData;
class	PgLay;
class	PgLayGlob;
class	PgLaySpc;
class	PgBlockDef;
class	PgBlockRef;
class	IDrawFile;
class	AtomicChg;

/*  DEFINITIONS    ========================================================= */
//
//
//
//
/*  CLASS    --------------------------------------------------------------- */

/**
*	@brief	A document represented inside Play Ground main library
*
*	A hidden file (FL_HIDDEN) does not generate events and should be ignored in
*	GUIs. It is used to load files without presenting them to the user.
*	The files are, however, listed at request.
*/
class CPGSHARED_EXPORT CpgDoc		: public QObject, public MemTrack		{
	Q_OBJECT
	BBM_TRACK( CpgDoc );
	
	//
	//
	//
	//
	/*  DEFINITIONS    ----------------------------------------------------- */


	/**
	*	@brief	flags
	*/
	enum		Flags		{
		FL_NONE = 0,				/**< no flag is set */

		FL_READ_ONLY	= 0x00000001,	/**< the document is read-only */
		FL_MODIFIED		= 0x00000002,	/**< the document was modified */
		FL_HIDDEN		= 0x00000004,	/**< the file is hidden */

		FL_MAX						/**< for validation */
	};

	friend class CpgLibrary;

	/*  DEFINITIONS    ===================================================== */
	//
	//
	//
	//
	/*  DATA    ------------------------------------------------------------ */

private:


	/**
	*	@brief	integer that uniquelly identifies this instance
	*/
	int							id_;


	/**
	*	@brief	the file that we're representing; may be empty
	*/
	QString						s_file_path_;


	/**
	*	@brief	the name
	*/
	QString						s_name_;


	/**
	*	@brief	various 1-bit switches
	*/
	Flags						flg_;


	/**
	*	@brief	undo stack
	*/
	QUndoStack					u_;



	/**
	*	@brief	the list of independent spaces
	*/
	QList<PgSpace*>				space_lst_;


	/**
	*	@brief	pointer to current space
	*/
	PgSpace *					crt_space_;


	/**
	*	@brief	the list of global layers
	*/
	QList<PgLayGlob*>			glob_ly_list_;


	/**
	*	@brief	the list of block definitions
	*/
	QList<PgBlockDef*>			blocks_;


	/**
	*	@brief	file format
	*/
	IDrawFile *					format_;



	/* User defined characteristics */

	/**
	*	@brief	the list of colors used by the drawing
	*/
	QList<PgColor*>				color_lst_;


	/**
	*	@brief	the list of line patterns used by the drawing
	*/
	QList<PgLnPattern*>			ln_patt_lst_;


	/**
	*	@brief	the list of line pattern scale used by the drawing
	*/
	QList<PgLnPattScale*>		ln_patt_scl_lst_;


	/**
	*	@brief	the list of line weights used by the drawing
	*/
	QList<PgLnWeight*>			ln_weight_lst_;



	/**
	*	@brief for new documents
	*/
	static int					untitled_cnt_;


	/*  DATA    ============================================================ */
	//
	//
	//
	//
	/*  FUNCTIONS    ------------------------------------------------------- */

public:


	/**
	*	@brief	constructor;
	*/
	CpgDoc				( bool b_vis, QObject * parent = NULL );


	/**
	*	@brief	destructor;
	*/
	virtual				~CpgDoc		( void );

	
	/**
	*	@brief	integer that uniquelly identifies this instance
	*/
	inline int				id							( void )
	{ return id_; }


public:


	/**
	*	@brief	the name of this document
	*/
	QString				documentName			( void ) const
	{ return s_name_; }

	/**
	*	@brief	changes the name of this document
	*
	*	This is a low level method that does not change any other internal status.
	*/
	void				setDocumentName			( const QString & new_val )
	{ s_name_ = new_val; }


	/**
	*	@brief	the path of the file associated with this document
	*/
	QString				filePath				( void ) const
	{ return s_file_path_; }


	/**
	*	@brief	changes the path of the file associated with this document
	*
	*	This is a low level method that does not change any other internal status.
	*/
	void				setFilePath				( const QString & new_val )
	{ s_file_path_ = new_val; }


	/**
	*	@brief	sets the name to an unique one for new documents
	*/
	inline void			setUnnamed				( void )
	{ s_name_ = QObject::tr( "Untitled %1").arg( id_ ); s_file_path_.clear(); }


	/**
	*	@brief	file format
	*/
	inline IDrawFile *		fileFormat					( void )
	{ return format_; }


public slots:


	/**
	*	@brief	save the content to given file or to a different file
	*
	*	As a result internally stored path changes. Modified flag is cleared.
	*/
	bool				fileSave				( const QString & f = QString() );


	/**
	*	@brief	initalises the view with a new, editable file
	*/
	bool				fileNew					( void );


	/**
	*	@brief	opens given local file
	*/
	bool				fileOpen				(
			const QString & f,
			const QString & loc = QString()
			);


	/**
	*	@brief	opens given resource; the content is not editable
	*/
	bool				openUrl					( const QUrl & url );


	/**
	*	@brief	inserts the text at current cursor position
	*/
	void				insertText				( const QString & text );


	/**
	*	@brief	inserts the html content at current cursor position
	*/
	void				insertHtml				( const QString & content );


	/**
	*	@brief	inserts a table at current cursor position
	*/
	void				insertTable				( void );


	/**
	*	@brief	inserts a column or more at current cursor position
	*
	*	-1 will insert a single column as first column.
	*	0 will insert a number of columns equal to the number of
	*	selected columns.
	*/
	void				insertTableCols			( int count = 1 );


	/**
	*	@brief	removes a column or more at current cursor position
	*/
	void				removeTableCols			( int count = 1 );


	/**
	*	@brief	inserts a row or more at current cursor position
	*
	*	-1 is a special value and inserts a header row.
	*	0 will insert a number of rows equal to the number of
	*	selected rows.
	*/
	void				insertTableRows			( int count = 1 );


	/**
	*	@brief	removes a row or more at current cursor position
	*/
	void				removeTableRows			( int count = 1 );


	/**
	*	@brief	inserts an image at current cursor position
	*/
	void				insertImage			( const QString & s_uri );


	/**
	*	@brief	cuts currently selected text
	*/
	void				cut					( void );


	/**
	*	@brief	copies currently selected text
	*/
	void				copy				( void );


	/**
	*	@brief	inserts the content of the clipboard
	*/
	void				paste				( void );


	/**
	*	@brief	undoes previous action
	*/
	void				undo				( void )
	{
		u_.undo();
	}


	/**
	*	@brief	redoes next action
	*/
	void				redo				( void )
	{
		u_.redo();
	}


	/**
	*	@brief	changes the fill color of the document or container / frame
	*/
	void				setFillColor		( const QColor & color );


	/**
	*	@brief	changes the color used to render primary things
	*/
	void				setPrimColor		( const QColor & color );

	/**
	*	@brief	find the text
	*/
	void				findNext			( const QString & s_text );


	/**
	*	@brief	find the text
	*/
	void				findPrev			( const QString & s_text );


	/**
	*	@brief	replace all occurances
	*/
	void				replaceAll			( const QString & s_find, const QString & s_repl );


	/**
	*	@brief	replace first occurance
	*/
	void				replaceNext			( const QString & s_find, const QString & s_repl );


	/**
	*	@brief	replace current occurance, if any
	*/
	void				replaceOne			( const QString & s_find, const QString & s_repl );



public:


	/**
	*	@brief	tell if there is an associated file or not (new or url)
	*/
	bool				isUntitled				( void ) const
	{
		return s_file_path_.isEmpty();
	}


	/**
	*	@brief	color for filling containers
	*/
	QColor				fillColor				( void );


	/**
	*	@brief	color for text
	*/
	QColor				primColor				( void );


	/**
	*	@brief	tell if the cursor is inside a table
	*/
	bool				inTable					( void ) const;


	/* ******************************************************************* */
	/** @name 	Undo / Redo related functionality                          */
	/* ******************************************************************* */
	///@{


public:


	/**
	*	@brief	undo-redo stack
	*
	*	For an comprehensive discussion about Undo/Redo functionality see
	*	\ref undo "dedicated page".
	*/
	inline QUndoStack *		undoStack			( void )
	{ return &u_; }


	/**
	*	@brief	appends a command to the undo stack
	*
	*	For an comprehensive discussion about Undo/Redo functionality see
	*	\ref undo "dedicated page".
	*/
	void					addUndoAtom			( AtomicChg * itm );


	/**
	*	@brief	start atoms compression in undo/redo framework
	*
	*	For an comprehensive discussion about Undo/Redo functionality see
	*	\ref undo "dedicated page".
	*/
	inline void				startCompressUndo	( const QString & s_text )
	{ u_.beginMacro( s_text ); }


	/**
	*	@brief	end atoms compression in undo/redo framework
	*
	*	For an comprehensive discussion about Undo/Redo functionality see
	*	\ref undo "dedicated page".
	*/
	inline void				endCompressUndo		( void)
	{ u_.endMacro(); }


	///@}
	/* ******************************************************************* */


	/**
	*	@brief	set the read only flag
	*/
	inline void			setReadOnly				( void )
	{ setFlag( FL_READ_ONLY ); }


	/**
	*	@brief	set the read only flag
	*/
	inline void			setReadOnly				( bool b )
	{ b ?  setReadOnly() : resetReadOnly(); }


	/**
	*	@brief	reset the read only flag
	*/
	inline void			resetReadOnly			( void )
	{ resetFlag( FL_READ_ONLY ); }


	/**
	*	@brief	tell if the document is read-only
	*/
	inline bool			isReadOnly				( void ) const
	{ return isFlag( FL_READ_ONLY ); }


	/**
	*	@brief	set the read only flag
	*/
	inline void			setModified				( void )
	{ setFlag( FL_MODIFIED ); }


	/**
	*	@brief	set the read only flag
	*/
	inline void			setModified				( bool b )
	{ b ?  setModified() : resetModified(); }


	/**
	*	@brief	reset the read only flag
	*/
	inline void			resetModified			( void )
	{ resetFlag( FL_MODIFIED ); }


	/**
	*	@brief	tell if the document is read-only
	*/
	inline bool			isModified				( void ) const
	{ return isFlag( FL_MODIFIED ); }



	/* ******************************************************************* */
	/** @name 	Flags manipulation                                         */
	/* ******************************************************************* */
	///@{


private:


	/**
	*	@brief	set (1) one of the boolean properties
	*/
	inline void		setFlag			( int new_flg )
	{ flg_ = static_cast<Flags> (flg_ | new_flg ); }


	/**
	*	@brief	set the value of the flag
	*/
	inline void		forceFlag		( int new_flg, bool b_val )
	{ if ( b_val ) setFlag( new_flg ); resetFlag( new_flg ); }


	/**
	*	@brief	reset (0) one of the boolean properties
	*/
	inline void		resetFlag		( int new_flg )
	{ flg_ = static_cast<Flags> (flg_ & (~new_flg )); }


	/**
	*	@brief	see if one boolean property is on or off
	*/
	inline bool		isFlag			( int new_flg ) const
	{ return (flg_ & new_flg ) != 0; }


	///@}
	/* ******************************************************************* */


	/* ******************************************************************* */
	/** @name 	Blocks related methods                                     */
	/* ******************************************************************* */
	///@{

protected:


	/**
	*	@brief	appends a block to the ones associated with this document
	*/
	void				addBlock			( PgBlockDef * itm );


	/**
	*	@brief	removes a block from the ones associated with this document
	*/
	void				remBlock			( PgBlockDef * itm );


public:


	/**
	*	@brief	return the list of blocks associated with this document
	*/
	inline QList<PgBlockDef*>	blocks		( void ) const
	{ return blocks_; }


	/**
	*	@brief	return the number of blocks in the document
	*/
	inline int				blockCount	( void ) const
	{ return blocks_.count(); }


	/**
	*	@brief	return the block in the document at given index
	*/
	inline PgBlockDef *		block		( int i ) const
	{
		if ( ( i < 0 ) || ( i > blocks_.count() ) )
			return NULL;
		return blocks_.at( i );
	}


	/**
	*	@brief	get the block that has this name
	*/
	PgBlockDef *				blockByName		(
			const QString &			s_name
			) const;


	///@}
	/* ******************************************************************* */



	/* ******************************************************************* */
	/** @name 	Spaces related methods                                     */
	/* ******************************************************************* */
	///@{


public:


	/**
	*	@brief	removes a space from the ones associated with this document
	*/
	void					remSpace			( PgSpace * itm );


	/**
	*	@brief	appends a space to the ones associated with this document
	*/
	void					addSpace			( PgSpace * itm );


	/**
	*	@brief	return the list of spaces associated with this document
	*/
	inline QList<PgSpace*>	spaces		( void ) const
	{ return space_lst_; }


	/**
	*	@brief	return the number of spaces in the document
	*/
	inline int				spaceCount	( void ) const
	{ return space_lst_.count(); }


	/**
	*	@brief	return the space in the document at given index
	*/
	inline PgSpace *		space		( int i ) const
	{
		if ( ( i < 0 ) || ( i > space_lst_.count() ) )
			return NULL;
		return space_lst_.at( i );
	}


	/**
	*	@brief	get the space that has this name
	*/
	PgSpace *				spaceByName		(
			const QString &			s_name
			) const;


	/**
	*	@brief	get the space that has this name
	*/
	inline int				spaceIndex		(
			PgSpace *				space
			) const
	{ return space_lst_.indexOf( space ); }


	/**
	*	@brief	get an unique space name
	*/
	QString					spaceUniqName	( void );


	/**
	*	@brief	get current space
	*/
	inline PgSpace *		currentSpace	( void ) const
	{ return crt_space_; }


	/**
	*	@brief	get current space
	*/
	inline int				currentSpaceIndex	( void ) const
	{ return space_lst_.indexOf( crt_space_ ); }


	/**
	*	@brief	change current space; may fail if space is foreign
	*/
	bool					setCurrentSpace	( PgSpace * new_val );


	/**
	*	@brief	change current space; may fail if space is foreign
	*/
	bool					setCurrentSpace	( int new_val )
	{
		if ( (new_val < 0 ) || ( new_val >= space_lst_.count() ) )
			return false;
		return setCurrentSpace( space_lst_.at( new_val ) );
	}


	///@}
	/* ******************************************************************* */


	/* ******************************************************************* */
	/** @name 	Layers related methods                                     */
	/* ******************************************************************* */
	///@{



	/**
	*	@brief	get the layer that has this name
	*/
	PgLayer *				layerByName			(
			const QString &				s_name
			) const;


	/**
	*	@brief	get an unique name for a layer
	*/
	QString					layerUniqName		( void ) const;


	/**
	*	@brief	return the list of layers associated with this document
	*/
	inline QList<PgLayGlob*>		layers		( void ) const
	{ return glob_ly_list_; }


	/**
	*	@brief	return the number of global layers
	*/
	inline int				layerCount		( void ) const
	{ return glob_ly_list_.count(); }


	/**
	*	@brief	return the list of layers associated with this document
	*/
	inline PgLayGlob*		layer			( int i ) const
	{
		if ( ( i < 0 ) || ( i >= glob_ly_list_.count() ) )
			return NULL;
		return glob_ly_list_.at( i );
	}


	/**
	*	@brief	copy the properties of layer at index i
	*/
	void					layerProps		( int i, LyData & d );


	/**
	*	@brief	add global layer
	*/
	inline int				addLayer	( PgLayGlob * new_ly)
	{ glob_ly_list_.append( new_ly ); return glob_ly_list_.count() - 1; }


	/**
	*	@brief	remove global layer
	*/
	inline int				remLayer	( PgLayGlob * layer )
	{
		int idx = glob_ly_list_.indexOf( layer );
		if ( idx == -1 )
		{
			Q_ASSERT( false );
			return false;
		}
		glob_ly_list_.removeAt( idx );
		return true;
	}


	/**
	*	@brief	remove global layer
	*/
	inline int				remLayer	( int itm )
	{
		if ( ( itm < 0 ) || ( itm >= glob_ly_list_.count() ) )
		{
			Q_ASSERT( false );
			return false;
		}
		glob_ly_list_.removeAt( itm );
		return true;
	}


	/**
	*	@brief	get instances from all spaces of a global layer
	*/
	QList<PgItList>			globalLayer		(
			int						idx
			) const;


	/**
	*	@brief	get the number of global layers
	*/
	inline int				globalLayersCount	( void ) const
	{ return glob_ly_list_.count(); }


	/**
	*	@brief	add global layer and do all related house keeping
	*/
	void					insertGlobalLayer	(
			PgLayGlob *					g_ly,
			QList<PgItList> &	l
			);


	/**
	*	@brief	remove global layer and do all related house keeping
	*/
	void					extractGlobalLayer	(
			PgLayGlob *					g_ly
			);


	/**
	*	@brief	creates a global layer, bypassing the undo-redo mechanism
	*/
	PgLayGlob *				createGlobalLayer	(
			const QString &				s_name
			);


	///@}
	/* ******************************************************************* */



	/* ******************************************************************* */
	/** @name 	Color related methods                                     */
	/* ******************************************************************* */
	///@{


public:


	/**
	*	@brief	return the list of colors associated with this document
	*/
	inline QList<PgColor*>	colors		( void ) const
	{ return color_lst_; }


	/**
	*	@brief	return the list of colors associated with this document
	*/
	inline int				colorsCount	( void ) const
	{ return color_lst_.count(); }


	/**
	*	@brief	add a color to the list of colors
	*/
	inline void				addColor	( PgColor * col )
	{
		if ( color_lst_.contains( col ) )
			return;
		color_lst_.append( col );
	}


	/**
	*	@brief	return the color at a particular index
	*/
	inline PgColor*			color		( int i ) const
	{
		if ( ( i < 0 ) || ( i >= color_lst_.count() ) )
			return NULL;
		return color_lst_.at( i );
	}


	///@}
	/* ******************************************************************* */



	/* ******************************************************************* */
	/** @name 	Line patters related methods                               */
	/* ******************************************************************* */
	///@{


public:


	/**
	*	@brief	return the list of line patterns associated with this document
	*/
	inline QList<PgLnPattern*>	lnPatts		( void ) const
	{ return ln_patt_lst_; }


	/**
	*	@brief	return the list of line patterns associated with this document
	*/
	inline int				lnPattCount	( void ) const
	{ return ln_patt_lst_.count(); }


	/**
	*	@brief	add a line pattern to the list
	*/
	inline void				addLnPatt	( PgLnPattern * col )
	{
		if ( ln_patt_lst_.contains( col ) )
			return;
		ln_patt_lst_.append( col );
	}


	/**
	*	@brief	return the line patterns at a particular index
	*/
	inline PgLnPattern *	lnPatt		( int i ) const
	{
		if ( ( i < 0 ) || ( i >= ln_patt_lst_.count() ) )
			return NULL;
		return ln_patt_lst_.at( i );
	}


	///@}
	/* ******************************************************************* */



	/* ******************************************************************* */
	/** @name 	Line pattern scale related methods                         */
	/* ******************************************************************* */
	///@{


public:


	/**
	*	@brief	return the list of line pattern scale associated with this document
	*/
	inline QList<PgLnPattScale*>	lnPattScales	( void ) const
	{ return ln_patt_scl_lst_; }


	/**
	*	@brief	return the list of line pattern scale associated with this document
	*/
	inline int				lnPattScaleCount	( void ) const
	{ return ln_patt_scl_lst_.count(); }


	/**
	*	@brief	add a line patter scale to the list
	*/
	inline void				addLnPattScale		( PgLnPattScale * col )
	{
		if ( ln_patt_scl_lst_.contains( col ) )
			return;
		ln_patt_scl_lst_.append( col );
	}


	/**
	*	@brief	return the line pattern scale at a particular index
	*/
	inline PgLnPattScale *	lnPattScale			( int i ) const
	{
		if ( ( i < 0 ) || ( i >= ln_patt_scl_lst_.count() ) )
			return NULL;
		return ln_patt_scl_lst_.at( i );
	}


	///@}
	/* ******************************************************************* */



	/* ******************************************************************* */
	/** @name 	Line weight scale related methods                          */
	/* ******************************************************************* */
	///@{


public:


	/**
	*	@brief	return the list of line pattern scale associated with this document
	*/
	inline QList<PgLnWeight*>	lnWeights	( void ) const
	{ return ln_weight_lst_; }


	/**
	*	@brief	return the list of line pattern scale associated with this document
	*/
	inline int				lnWeightCount	( void ) const
	{ return ln_weight_lst_.count(); }


	/**
	*	@brief	add a line patter scale to the list
	*/
	inline void				addLnWeight		( PgLnWeight * col )
	{
		if ( ln_weight_lst_.contains( col ) )
			return;
		ln_weight_lst_.append( col );
	}


	/**
	*	@brief	return the line pattern scale at a particular index
	*/
	inline PgLnWeight *		lnWeight		( int i ) const
	{
		if ( ( i < 0 ) || ( i >= ln_weight_lst_.count() ) )
			return NULL;
		return ln_weight_lst_.at( i );
	}


	///@}
	/* ******************************************************************* */



	/* ******************************************************************* */
	/** @name 	Hidden flag                                                */
	/* ******************************************************************* */
	///@{

protected:


	/**
	*	@brief	set the hidden flag
	*/
	inline void		setHidden			( void )
	{ setFlag( FL_HIDDEN ); }


	/**
	*	@brief	reset the hidden flag
	*/
	inline void		resetHidden			( void )
	{ resetFlag( FL_HIDDEN ); }


	/**
	*	@brief	tell if the document is hidden
	*/
	inline bool		isHidden			( void )
	{ return isFlag( FL_HIDDEN ); }


	///@}
	/* ******************************************************************* */


private:


	/**
	*	@brief	used by initialisation methods to do common preparations
	*/
	void			prepareDocument			(
			const QString &				file_name = QString(),
			bool						is_url = false
			);


signals:


	/**
	*	@brief	the document (address, file, new file) has Modified
	*
	*	This results from functions such as fileNew(),
	*	fileSave(), fileOpen(), openUrl().
	*/
	void			fileNameModified			( const QString & new_name );


	/**
	*	@brief	informing others that the content changed
	*
	*	contentsChanged() is implementation specific and should not be relied upon.
	*/
	void			contentModified				( void );



	
	/*  FUNCTIONS    ======================================================= */
	//
	//
	//
	//

};	/*	class CpgDoc	*/

/*  CLASS    =============================================================== */
//
//
//
//

}	//	namespace	cpg

#endif // __CPGDOC_INC__
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
